﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.IO.Compression;

namespace NSEEOD_downloader.Tasks
{
    public abstract class DownloadTask
    {
        public event EventHandler OnNewMessage;
        public event EventHandler OnComplete;
        public string DownloadDir = "";
        
        protected WebClient net = new WebClient();
        protected DateTime startDate = new DateTime();
        protected DateTime endDate = new DateTime();

        private string lastLinkStr = "";
        private string lastDate = "";
        
        public DownloadTask()
        {
            net.DownloadDataCompleted += new DownloadDataCompletedEventHandler(net_DownloadDataCompleted);            
        }
        
        protected void AppendStatus(string Message)
        {
            if (null != OnNewMessage)
            {
                OnNewMessage.Invoke(Message, null);
            }
        }

        protected abstract string ConvertDownloadedDataToCurrentTaskData(string DownloadedData);
        protected abstract string ParseLink(string link, DateTime date);

        protected abstract string GetLinkText();
               

        protected void ProcessDownloadedData(byte []downloadedData)
        {
            string DestFileName = lastLinkStr;
            if (lastLinkStr.LastIndexOf('/') > 3)
            {
                DestFileName = lastLinkStr.Substring(lastLinkStr.LastIndexOf('/') + 1);
            }
            bool CompressedFile = false;
            if (lastLinkStr.LastIndexOf('.') > lastLinkStr.LastIndexOf('/'))
            {
                if (lastLinkStr.ToLower().IndexOf(".zip") == (lastLinkStr.Length - 4))
                {
                    CompressedFile = true;
                }
                DestFileName = DestFileName.Substring(0, DestFileName.LastIndexOf('.')) + ".txt";
            }

            string MSEODData = "";
            if (true == CompressedFile)
            {
                MemoryStream ms = new MemoryStream();
                ms.Write(downloadedData, 0, downloadedData.Length);

                // Reset the memory stream position to begin decompression.
                ms.Position = 0;

                AppendStatus("Uncompressing...");
                GZipStream zipStream = new GZipStream(ms, CompressionMode.Decompress);
                string NSEEODData = "";
                Utils.ReadAllBytesFromStream(zipStream, ref NSEEODData);
                MSEODData = ConvertDownloadedDataToCurrentTaskData(NSEEODData);
            }
            else
            {
                string NSEEODData = System.Text.Encoding.ASCII.GetString(downloadedData);
                MSEODData = ConvertDownloadedDataToCurrentTaskData(NSEEODData);
            }
            try
            {
                StreamWriter TextFile = new StreamWriter(DownloadDir + "\\" + DestFileName);
                TextFile.Write(MSEODData);
                TextFile.Close();
                AppendStatus("Created " + DownloadDir + "\\" + DestFileName);

                //Start the next download process
                if (startDate < endDate)
                {
                    startDate = startDate.AddDays(1);
                    DownloadData(startDate);
                }
                else
                {
                    AppendStatus("Download completed.");                    
                    if (null != OnComplete)
                    {
                        OnComplete.Invoke(null, null);
                    }
                }
            }
            catch (Exception err)
            {
                AppendStatus("Err " + err.Message);
            }
        }        

        public void CancelAsync()
        {
            if (net.IsBusy)
            {
                net.CancelAsync();
            }
        }

        /// <summary>
        /// true: An error is handled
        /// false: there is no error generated
        /// </summary>
        /// <returns></returns>
        protected bool BasicErrorHandling(DownloadDataCompletedEventArgs e)
        {
            bool handled = false;
            if (null != e.Error && e.Error.Message.IndexOf("404") >= 0)
            {
                AppendStatus("Data not available for " + lastDate);
                handled = true;
                //Start the next download process
                if (startDate < endDate)
                {
                    startDate = startDate.AddDays(1);
                    DownloadData(startDate);
                }
                else
                {
                    AppendStatus("Download completed.");
                    if (null != OnComplete)
                    {
                        OnComplete.Invoke(null, null);
                    }
                }
            }
            else if (true == e.Cancelled || null != e.Error)
            {
                handled = true;
                if (null != e.Error)
                {
                    AppendStatus(e.Error.Message);
                }
                AppendStatus("Download cancelled.");
                if (null != OnComplete)
                {
                    OnComplete.Invoke(null, null);
                }
            }
            return handled;
        }

        void net_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            bool error = BasicErrorHandling(e);
            if (false == error && false == e.Cancelled)
            {
                ProcessDownloadedData(e.Result);
            }

        }

        protected void DownloadData(DateTime date)
        {
            try
            {
                AppendStatus("");
                string CurrentDownloadLink = ParseLink(GetLinkText(), date);
                CurrentDownloadLink = CurrentDownloadLink.Trim();
                AppendStatus("Processing: " + CurrentDownloadLink);
                lastDate = date.ToShortDateString();
                Uri uri = new Uri(CurrentDownloadLink);
                net.DownloadDataAsync(uri);
                lastLinkStr = CurrentDownloadLink;
                //http://www.nseindia.com/content/historical/EQUITIES/2007/Dec/cm31Dec2007bhav.csv                
            }
            catch (Exception err)
            {
                AppendStatus(err.Message);
                if (null != OnComplete)
                {
                    OnComplete.Invoke(null, null);
                }
            }
        }

        public void Download(DateTime startDate, DateTime endDate)
        {
            this.startDate = startDate;
            this.endDate = endDate;
            DownloadData(this.startDate);
        }
    }
}
